package com.hua.androidibeaconlib;

import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAdapter.LeScanCallback;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

import java.util.*;
/**
 * 定时提供iBeacon的RSSI值，并且可以设置获取RSSI的时间间隔为200到2000毫秒，
 * 如果超出这个范围则自动设置成默认值1000毫秒
 * @author liuzhenhua
 *
 */
public class IBeaconService  extends Service{

	public class IBeaconBinder extends Binder {
		public IBeaconService getService() {
			return IBeaconService.this;
		}
	}
	private Object syn_lock=new Object();
	private IBeaconConsumer mConsumer=null;
	private final IBeaconBinder mBinder=new IBeaconBinder();
	private BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter(); 
	volatile private Map<IBeacon,IBeaconWithCount> mIBeacons=new HashMap<IBeacon,IBeaconWithCount>();
	volatile private Map<IBeacon,IBeaconWithCount> mLastIBeacons=new HashMap<IBeacon,IBeaconWithCount>();
	private int period; //返回扫描得到的ibeacon的结果，单位毫秒，默认扫描获取iBeacon的RSSI的时间间隔为1000毫秒
	private TimerTask mTask;
	@Override
	public IBinder onBind(Intent intent) {
        boolean result = mAdapter.startLeScan(mCallback);
        LogUtil.printMainActivity("结果："+result);
		return mBinder;
	}
	
	/**
	 * 设置扫描iBeacon时间间隔
	 * @param period 扫描间隔，单位毫秒，范围100到2000，当不在此范围时将采用默认值1000
	 */
	public void setScanInterval(int period){
		
		this.period=period;
		if(period<100 || period>2000){
			this.period=100;
		}
		if(mTask !=null ){
			mTask.cancel();
		}

		mTask=new MyTimerTask();
		mTimer.scheduleAtFixedRate(mTask, 1l, this.period);//设置扫描间隔后重新启动定时器定期执行回调onFoundIBeacon

	}
	
	public void setConsumer(IBeaconConsumer consumer){
		this.mConsumer=consumer;
	}

	//蓝牙4.0扫描回调类
	private LeScanCallback mCallback = new LeScanCallbackImp();
    private final class LeScanCallbackImp implements LeScanCallback {
		@Override
		public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
			String address=device.getAddress();
            //调试蓝牙地址
            LogUtil.printLE(address);
			StringBuilder uuid_builder=new StringBuilder(18);//iBeacon的uuid的长度为16个字节，所以建立StringBuilder时就设置初始化容量
			//提取uuid
			for (int i = 9; i <25 ; i++) {   
				String hex = Integer.toHexString(scanRecord[i] & 0xFF);   //将字节转换成十六进制
				if (hex.length() == 1) {   
					hex = '0' + hex;   
				}   
				uuid_builder.append(hex.toUpperCase() ) ;   
			}  
			String uuid=uuid_builder.toString();

			//提取major
			int major=(scanRecord[25]<<8)& 0xFF00 |(scanRecord[26]& 0xFF);

			//提取minor
			int minor=(scanRecord[27]<<8)& 0xFF00 |(scanRecord[28]& 0xFF);

			//提取Tx-power
			int tx_power= scanRecord[29];

            LogUtil.printLE("id: "+uuid+major+minor);
			IBeacon ibeacon=new IBeacon(address, uuid, major, minor, rssi, tx_power);
			synchronized (syn_lock) {
				mIBeacons.put(ibeacon,new IBeaconWithCount(ibeacon));

			}
		}

    }

    /**
     * 用来定时获取iBeacon数据
     */
	private Timer mTimer = new Timer();
	
	//用来定时回调IBeaconConsumer.onFoundIBeacon(……）方法
	private class MyTimerTask extends TimerTask{
		@Override
		synchronized public void run() {
			if(mConsumer ==null)return;
			
			//另外定义一个TreeSet<IBeacon>变量指向mIBeacons，而mIBeacons重新new 一个，使得mIBeacons中存储的仅是最新一次扫描的结果，
			//同时还可以避免并发异常
			
			Map<IBeacon,IBeaconWithCount> ibeacons=mIBeacons;
			synchronized (syn_lock) {
				mIBeacons=new HashMap<IBeacon,IBeaconWithCount>();

				IBeaconWithCount tmp_ibeacon;
				for(IBeacon ibeacon : mLastIBeacons.keySet()){
					//最后得到的iBeacon集合是低通滤波后的结果，时间刻度上越早收集到的iBeacon所占的权重越小
					if(ibeacons.containsKey(ibeacon)){
						tmp_ibeacon=ibeacons.get(ibeacon);
						tmp_ibeacon.setCount(0);
						tmp_ibeacon.setRssi( (tmp_ibeacon.getRssi()+ibeacon.getRssi())/2  );
					}else{
						tmp_ibeacon= mLastIBeacons.get(ibeacon);

						//如果某个iBeacon在三次都没有扫描到，则说明该iBeacon可能已经不在扫描范围内了
						if(tmp_ibeacon.getCount()<3){
							tmp_ibeacon.setCount(tmp_ibeacon.getCount()+1);//“未扫描到次数”加 1
							ibeacons.put(ibeacon, tmp_ibeacon); //将上次的ibeacon数据复制到这次的数据集中
						}

					}

				}

			}
			mLastIBeacons=ibeacons;
			Collection<IBeacon> para_ibeacons=new TreeSet<IBeacon>();
			for(IBeacon ibeacon : ibeacons.values()){
				para_ibeacons.add(ibeacon);
			}
			mConsumer.onFoundIBeacon(para_ibeacons);
		}
	}

	@Override
	public void onCreate() {
		super.onCreate();
	}

	@Override
	public void onDestroy() {
		System.out.println("service destroy");
        super.onDestroy();
	}

    @Override
    public boolean onUnbind(Intent intent) {
        mTask.cancel();
		mTimer.cancel();
        return super.onUnbind(intent);
    }
}


/**
 * 设计说明：（2014年4月1日晚）
 * 1.蓝牙回调函数每调用一次，就解析出一个iBeacon数据，并把这个iBeacon放进Map<IBeacon,IbeaconWithCount> mIBeacons中
 * 2.到了设定的取数据时间（由定时器设定），就定义 Map<IBeacon,IbeaconWithCount> ibeacons=mIBeacons 取得mIBeacons中的值，
 *  此时再将mIBeacons=new HashMap<IBeacon,IbeaconWithCount>() ,这样就保证了mIBeacons可以继续在蓝牙回调函数中收集iBeacon数据而不会发生并发异常，
 *  如果不这么做，mIBeacons在会在蓝牙回调函数内收集数据的同时也在TimerTask的run方法中遍历，由于这两个方法不在同一个线程中，所以会发生并发异常
 * 3.接着对比mLastIBeacons和 ibeacons中的数据，mLastIBeacons中存放的是之前收集到的iBeacon数据集合，如果mLastIBeacons中的一些iBeacon在ibeacons没有，
 *  则可能是由于发生了碰撞，导致本次取数据间隔内没有扫描到该iBeacon，所以就把上一次的iBeacon数据放入到ibeacons当做本次的iBeacon数据，
 *  如果mLastIBeacons中的iBeacon同时也在ibeacons中，那么求这两个数据集中同一个iBeacon的rssi的平均值作为本次的iBeacon的rssi值
 * 4.按照3中的策略可能导致一个问题：如果某个iBeacon已经不在扫描范围内了，但是由于总是取上一次的iBeacon值作为本次的值，会导致jishiBeacon已经不在扫描
 *  范围内，但是仍然会取得该iBeacon。
 * 5.根据4中的分析，则增加一个计数器（这里是通过IBeaconWithCount继承IBeacon实现的），用来记录某个iBeacon已经连续多少次没有被扫描到了，
 *  当该计数器超过阈值就判断它已经不在扫描范围内了，就不将它加入本次扫描结果集中
 * 6.另外，为了使RSSI值相对比较稳定，采用了将缓存了的RSSI值与当前扫描到的RSSI值求平均，这样就使得时间越久的RSSI值对最后的影响越小，而越靠近当前时间
 *  收集到的RSSI在最后结果RSSI中所占比重越大。
 */
